#pragma once

#include <iostream>
#include <unordered_map>
#include <string>
#include <cassert>
#include <vector>
#include <fstream>
#include <cstdlib>

#include "../comm/util.hpp"
#include "../comm/log.hpp"
#include "../include/mysql/mysql.h"

namespace ns_model
{
    using namespace std;
    using namespace ns_log;
    using namespace ns_util;

    struct Question
    {
        std::string number; // 题号
        std::string title;  // 标题
        std::string star;   // 难度：简单、中等、困难
        std::string desc;   // 题目描述
        std::string header; // 题目预设给用户的在线编辑器代码
        std::string tail;   
        int cpu_limit;     
        int mem_limit;      
    };

    const string oj_questions = "oj_questions"; // MySQL中的表名
    const string host = "127.0.0.1";
    const string user = "oj_client"; // 后序考虑使用环境变量替换明文user和password
    const string password = "tmppassword"; 
    const string db = "oj";
    const int port = 3306;

    class Model
    {
    public:
        Model() {}
        ~Model() {}

        bool QueryMySQL(const string &sql, vector<Question> *out)
        {
            // 创建MySQL句柄
            MYSQL *my = mysql_init(nullptr);

            // 连接MySQL
            if (mysql_real_connect(my, host.c_str(), user.c_str(), password.c_str(), db.c_str(), port, nullptr, 0) == nullptr)
            {
                LOG(FATAL) << "连接数据库失败！！！" << "\n";
                return false;
            }

            // 设置该链接的编码格式，否则容易乱码
            mysql_set_character_set(my, "utf8");

            LOG(INFO) << "连接数据库成功" << "\n";

            // 执行sql语句
            if (mysql_query(my, sql.c_str()) != 0)
            {
                LOG(WARNING) << sql << "执行失败" << "\n";
                return false;
            }

            // 提取结果
            MYSQL_RES *res = mysql_store_result(my);

            // 分析结果
            int rows = mysql_num_rows(res);   // 行数量
            int cols = mysql_num_fields(res); // 列数量

            Question q;
            for (int i = 0; i < rows; i++)
            {
                MYSQL_ROW row = mysql_fetch_row(res);
                q.number = row[0];
                q.title = row[1];
                q.star = row[2];
                q.desc = row[3];
                q.header = row[4];
                q.tail = row[5];
                q.cpu_limit = atoi(row[6]);
                q.mem_limit = atoi(row[7]);

                out->push_back(q);
            }

            free(res);
            mysql_close(my);

            return true;
        }

        bool GetAllQuestions(vector<Question> *out)
        {
            string sql = "select * from ";
            sql += oj_questions;
            return QueryMySQL(sql, out);
        }

        bool GetOneQuestion(const string &number, Question *q)
        {
            bool res = false;
            string sql = "select * from ";
            sql += oj_questions;
            sql += " where number = ";
            sql += number;
            vector<Question> result;
            if (QueryMySQL(sql, &result))
            {
                if (result.size() == 1)
                {
                    *q = result[0];
                    res = true;
                }
            }
            return res;
        }
    };

    class UserModel
    {
    public:
        UserModel()
        {
            // 连接数据库
            conn = mysql_init(nullptr);
            if (!conn)
            {
                std::cerr << "mysql_init() failed" << std::endl;
                exit(EXIT_FAILURE);
            }

            if (!mysql_real_connect(conn, host.c_str(), user.c_str(), password.c_str(), "UserDB", port, nullptr, 0))
            {
                std::cerr << "mysql_real_connect() failed: " << mysql_error(conn) << std::endl;
                mysql_close(conn);
                exit(EXIT_FAILURE);
            }
        }

        ~UserModel()
        {
            // 关闭数据库连接
            mysql_close(conn);
        }

        // 注册用户
        bool registerUser(const std::string &username, const std::string &password)
        {
            std::string query = "INSERT INTO users (username, password) VALUES ('" + username + "', '" + password + "')";
            if (mysql_query(conn, query.c_str()))
            {
                std::cerr << "MySQL query error: " << mysql_error(conn) << std::endl;
                return false;
            }
            return true;
        }

        // 验证用户登录
        bool authenticateUser(const std::string &username, const std::string &password)
        {
            std::string query = "SELECT id, password FROM users WHERE username = '" + username + "'";
            if (mysql_query(conn, query.c_str()))
            {
                std::cerr << "MySQL query error: " << mysql_error(conn) << std::endl;
                return false;
            }

            MYSQL_RES *res = mysql_store_result(conn);
            if (!res)
            {
                std::cerr << "MySQL result error: " << mysql_error(conn) << std::endl;
                return false;
            }

            MYSQL_ROW row = mysql_fetch_row(res);
            if (row && row[1] == password)
            {
                mysql_free_result(res);
                return true;
            }
            else
            {
                mysql_free_result(res);
                return false;
            }
        }

        // 更新会话Token
        bool updateSessionToken(int userId, const std::string &token)
        {
            std::string query = "UPDATE users SET session_token = '" + token + "' WHERE id = " + std::to_string(userId);
            if (mysql_query(conn, query.c_str()))
            {
                std::cerr << "MySQL query error: " << mysql_error(conn) << std::endl;
                return false;
            }
            return true;
        }

        // 根据Session Token获取用户名
        std::string getUsernameByToken(const std::string &sessionToken)
        {
            std::string query = "SELECT username FROM users WHERE session_token = '" + sessionToken + "'";
            if (mysql_query(conn, query.c_str()))
            {
                std::cerr << "MySQL query error: " << mysql_error(conn) << std::endl;
                return "";
            }

            MYSQL_RES *res = mysql_store_result(conn);
            if (!res)
            {
                std::cerr << "MySQL result error: " << mysql_error(conn) << std::endl;
                return "";
            }

            MYSQL_ROW row = mysql_fetch_row(res);
            if (row)
            {
                std::string username = row[0];
                mysql_free_result(res);
                return username;
            }
            else
            {
                mysql_free_result(res);
                return "";
            }
        }

    private:
        MYSQL *conn;
    };
}
